﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Input;

namespace System.Controls.Extensions
{
  public static class TextBoxMoveFocus
  {
    public static void TryMoveFocus( this TextBox textBox, KeyEventArgs args )
    {
      if ( args.Key == Key.Left && ( Keyboard.Modifiers == ModifierKeys.None || Keyboard.Modifiers == ModifierKeys.Control ) )
      {
        args.Handled = textBox.MoveFocusLeft();
        return;
      }

      if ( args.Key == Key.Right && ( Keyboard.Modifiers == ModifierKeys.None || Keyboard.Modifiers == ModifierKeys.Control ) )
      {
        args.Handled = textBox.MoveFocusRight();
        return;
      }

      if ( ( args.Key == Key.Up || args.Key == Key.PageUp ) && ( Keyboard.Modifiers == ModifierKeys.None || Keyboard.Modifiers == ModifierKeys.Control ) )
      {
        args.Handled = textBox.MoveFocusUp();
        return;
      }

      if ( ( args.Key != Key.Down && args.Key != Key.PageDown ) || ( Keyboard.Modifiers != ModifierKeys.None && Keyboard.Modifiers != ModifierKeys.Control ) )
        return;

      args.Handled = textBox.MoveFocusDown();
    }

    private static bool MoveFocusDown( this TextBox textBox )
    {
      var lineNumber = textBox.GetLineIndexFromCharacterIndex( textBox.SelectionStart );

      if ( lineNumber != ( textBox.LineCount - 1 ) )
        return false;

      if ( ComponentCommands.MoveFocusDown.CanExecute( null, textBox ) )
      {
        ComponentCommands.MoveFocusDown.Execute( null, textBox );
        return true;
      }

      if ( !textBox.CanMoveFocus( FocusNavigationDirection.Down, false ) )
        return false;

      textBox.MoveFocus( new TraversalRequest( FocusNavigationDirection.Down ) );
      return true;
    }

    private static bool MoveFocusUp( this TextBox textBox )
    {
      var lineNumber = textBox.GetLineIndexFromCharacterIndex( textBox.SelectionStart );

      if ( lineNumber != 0 )
        return false;

      if ( ComponentCommands.MoveFocusUp.CanExecute( null, textBox ) )
      {
        ComponentCommands.MoveFocusUp.Execute( null, textBox );
        return true;
      }

      if ( !textBox.CanMoveFocus( FocusNavigationDirection.Up, false ) )
        return false;

      textBox.MoveFocus( new TraversalRequest( FocusNavigationDirection.Up ) );
      return true;
    }

    private static bool MoveFocusRight( this TextBox textBox )
    {
      if ( textBox.FlowDirection == FlowDirection.LeftToRight )
      {
        if ( textBox.CaretIndex != textBox.Text.Length || textBox.SelectionLength != 0 )
          return false;

        if ( ComponentCommands.MoveFocusForward.CanExecute( null, textBox ) )
        {
          ComponentCommands.MoveFocusForward.Execute( null, textBox );
          return true;
        }

        if ( !textBox.CanMoveFocus( FocusNavigationDirection.Right, false ) )
          return false;

        textBox.MoveFocus( new TraversalRequest( FocusNavigationDirection.Right ) );
        return true;
      }

      if ( textBox.CaretIndex != 0 || textBox.SelectionLength != 0 )
        return false;

      if ( ComponentCommands.MoveFocusForward.CanExecute( null, textBox ) )
      {
        ComponentCommands.MoveFocusForward.Execute( null, textBox );
        return true;
      }

      if ( !textBox.CanMoveFocus( FocusNavigationDirection.Right, false ) )
        return false;

      textBox.MoveFocus( new TraversalRequest( FocusNavigationDirection.Right ) );
      return true;
    }

    private static bool MoveFocusLeft( this TextBox textBox )
    {
      if ( textBox.FlowDirection == FlowDirection.LeftToRight )
      {
        if ( textBox.CaretIndex != 0 || textBox.SelectionLength != 0 )
          return false;

        if ( ComponentCommands.MoveFocusBack.CanExecute( null, textBox ) )
        {
          ComponentCommands.MoveFocusBack.Execute( null, textBox );
          return true;
        }

        if ( !textBox.CanMoveFocus( FocusNavigationDirection.Left, false ) )
          return false;

        textBox.MoveFocus( new TraversalRequest( FocusNavigationDirection.Left ) );
        return true;
      }

      if ( textBox.CaretIndex != textBox.Text.Length || textBox.SelectionLength != 0 )
        return false;

      if ( ComponentCommands.MoveFocusBack.CanExecute( null, textBox ) )
      {
        ComponentCommands.MoveFocusBack.Execute( null, textBox );
        return true;
      }

      if ( !textBox.CanMoveFocus( FocusNavigationDirection.Left, false ) )
        return false;

      textBox.MoveFocus( new TraversalRequest( FocusNavigationDirection.Left ) );
      return true;
    }

    internal static bool CanMoveFocus( this TextBox textBox, FocusNavigationDirection direction, bool reachedMax )
    {
      var args = new QueryMoveFocusEventArgs( direction, reachedMax );
      textBox.RaiseEvent( args );
      return args.CanMoveFocus;
    }
  }
}
